﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Compiler.TableOfSymbols;
using Compiler.TableOfSymbols.Concepts;
using Compiler.TableOfSymbols.Types;
using Compiler.Unification.Constraints;
using Compiler.Unification.Substitutions;

namespace Compiler.CompilerAPI.Contexts
{
    /// <summary>
    /// Контекст концепта
    /// </summary>
    public class ConceptContext : Context
    {
        private ConceptDefinition conceptData;
        private SymbolTable symbolTable;

        /// <summary>
        /// Множество типовых ограничений 
        /// </summary>
        private HashSet<BaseTypeConstraint> typeConstraints;

        public string ConceptName
        { 
            get { return conceptData.Name; } 
        }
        public ConceptDefinition ConceptData
        {
            get { return conceptData; }
        }

        public HashSet<BaseTypeConstraint> TypeConstraints
        { 
            get { return typeConstraints; } 
        }

        public ConceptContext(NETTypesBijectiveMap typesBijectiveMap, ConceptDefinition conceptData)
            : base(typesBijectiveMap)
        {
            System.Diagnostics.Debug.Assert(conceptData != null,
                String.Format("conceptData can not be null"));
            this.conceptData = conceptData;
            symbolTable = new SymbolTable();
            typeConstraints = new HashSet<BaseTypeConstraint>();
        }

        /// <summary>
        /// Ищет в контексте информацию о типе
        /// </summary>
        /// <param name="name">Имя типа</param>
        /// <param name="qualifier">Квалификатор пространств имён</param>
        /// <returns>Информацию о результатах поиска (включает тип, если найден)</returns>
        public override TypeSearchInfo TryGetTypeInfo(string name, string qualifier)
        {
            if (qualifier == "")
            {
                if (!symbolTable.Contains(name))
                    return new TypeSearchInfo(true, false);
                BaseType nameData = symbolTable[name] as BaseType;
                if (nameData == null)
                    return new TypeSearchInfo(true, false);
                return new TypeSearchInfo(nameData);
            }
            else
                // TODO
                return new TypeSearchInfo(true, false);
        }

        /// <summary>
        /// Проверяет, определена ли в контексте какая-нибудь 
        /// структурная единица с именем name
        /// </summary>
        /// <param name="name">Имя</param>
        /// <returns>Истину, если в контексте определена единица с именем name</returns>
        public override bool ContainsDefinedName(string name)
        {
            System.Diagnostics.Debug.Assert((name != null) && (name != ""),
                String.Format("name can not be empty"));
            return symbolTable.Contains(name);
        }

        /// <summary>
        /// Добавляет в контекст определение новой структурной единицы
        /// (недопустимо в данном контексте)
        /// </summary>
        /// <param name="name">Имя новой единицы</param>
        /// <param name="data">Данные структурной единицы</param>
        public override void AddNewDefinition(string name, SymbolTableData data)
        {
            throw new InvalidOperationException(
                "В контекст концепта нельзя добавить произвольное определение");
        }

        public TypeVariable AddConceptTypeParameter(string name)
        {
            System.Diagnostics.Debug.Assert((name != null) && (name != ""),
                String.Format("name can not be empty"));
            TypeVariable newTypeVar = conceptData.AddNewTypeParameter(name);
            symbolTable.Add(name, newTypeVar);
            return newTypeVar;
        }

        public TypeVariable AddConceptAssociatedType(string name)
        {
            System.Diagnostics.Debug.Assert((name != null) && (name != ""),
                String.Format("name can not be empty"));
            TypeVariable newTypeVar = conceptData.AddNewAssociatedType(name);
            symbolTable.Add(name, newTypeVar);
            return newTypeVar;
        }

        public void AddTypeConstraint(BaseTypeConstraint newConstraint)
        {
            System.Diagnostics.Debug.Assert(newConstraint != null,
                String.Format("newConstraint can not be null"));
            typeConstraints.Add(newConstraint);
        }

        public void AddTypeAlias(string name, BaseType typeInfo)
        {
            System.Diagnostics.Debug.Assert((name != null) && (name != ""),
               String.Format("name can not be empty"));
            System.Diagnostics.Debug.Assert(typeInfo != null,
                String.Format("typeInfo can not be null"));
            symbolTable.Add(name, typeInfo);
        }

        public void ApplySubstitution(Substitution subs)
        {
            this.symbolTable.ApplySubstitution(subs);
            this.conceptData.ApplySubstitution(subs);
        }

        public void UpdateConstraints(HashSet<BaseTypeConstraint> constraints)
        {
            System.Diagnostics.Debug.Assert(constraints != null,
               String.Format("constraints can not be null"));
            this.conceptData.SetConstraints(constraints);
        }
    }
}
